package yui.comn.mybatisx.extension.methods;

import static java.util.stream.Collectors.joining;

import java.lang.reflect.Field;

import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;

import yui.comn.mybatisx.core.InjectorMapperAnnotationAssistant;
import yui.comn.mybatisx.core.enums.SoftSqlMethod;
import yui.comn.mybatisx.core.toolkit.MybatisLinkConstants;

/**
 * <p>
 * 抽象的注入方法类
 * </p>
 *
 * @author yuyi (1060771195@qq.com)
 */
public abstract class AbstractSoftMethod extends AbstractMethod {
    private static final long serialVersionUID = -8889492920740410293L;

    /**
     * 注入自定义方法
     */
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        
        parseResultMap(builderAssistant, mapperClass, modelClass, tableInfo);
        
        injectMappedStatement(mapperClass, modelClass, tableInfo);
    }
    
    /**
     * 通过反射机制，通过属性名称，实例化对象中的对象(该对象不是基本类型，一般是自定义对象)
     */
    public void parseResultMap(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, 
                               Class<?> modelClass, TableInfo tableInfo) {
        if (StringUtils.isNotBlank(tableInfo.getResultMap())) {
            return;
        }
        InjectorMapperAnnotationAssistant resultMapBuilder = new InjectorMapperAnnotationAssistant(
                configuration, builderAssistant, mapperClass);
        String resultMapId = resultMapBuilder.parseResultMapp(modelClass);
        logger.debug("resultMapId --> " + resultMapId);
        // tableInfo.setResultMap(resultMapId);
        initTableResultMap(tableInfo, resultMapId);
    }
    
    @SuppressWarnings("rawtypes")
    private void initTableResultMap(TableInfo tableInfo, String resultMapId) {
        try {
            // 获取obj类的字节文件对象
            Class c = tableInfo.getClass();
            // 获取该类的成员变量
            Field f = c.getDeclaredField("resultMap");
            // 取消语言访问检查
            f.setAccessible(true);
            // 给变量赋值
            f.set(tableInfo, resultMapId);
         } catch (Exception e) {
         }
    }
    
    /**
     * 插入resultMap查询ms
     */
    protected MappedStatement addSelectMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource, String resultMap) {
        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, resultMap, null,
                new NoKeyGenerator(), null, null);
    }
    
    /**
     * 插入resultType查询ms
     */
    protected MappedStatement addSelectMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource, Class<?> resultType) {
        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, null, resultType,
                new NoKeyGenerator(), null, null);
    }
    
    /**
     * <p>
     * SQL 更新 set 语句
     * </p>
     *
     * @param table 表信息
     * @return sql set 片段
     */
    protected String sqlLogicSet(TableInfo table) {
        return "SET " + table.getLogicDeleteSql(false, true);
    }
    
    protected String getAliasTable(TableInfo table) {
        return table.getTableName() + StringPool.SPACE + table.getTableName();
    }
    
    protected String getAliasLogicDeleteSql(TableInfo table) {
        String logicDeleteSql = table.getLogicDeleteSql(false, false);
        if (StringUtils.isNotBlank(logicDeleteSql)) {
            return " AND " + logicDeleteSql;
        }
        return "";
    }

    
    /**
     * <p>
     * SQL 查询所有 表名.字段 
     * 如 t_sys_user.username
     * </p>
     *
     * @param table        表信息
     * @param queryWrapper 是否为使用 queryWrapper 查询
     * @return sql 脚本
     */
    protected String sqlSelectAliasColumns(TableInfo table, boolean queryWrapper) {
        /* 假设存在 resultMap 映射返回 */
        String selectColumns = StringPool.ASTERISK;
        
        /* 普通查询 */
        selectColumns = table.getAllSqlSelect();
        
        StringBuffer aliasColums = new StringBuffer();
        String[] columns = selectColumns.split(StringPool.COMMA);
        for (String column : columns) {
            if (aliasColums.length() > 0) {
                aliasColums.append(StringPool.COMMA).append(StringPool.SPACE);
            }
            aliasColums.append(table.getTableName()).append(StringPool.DOT)
                    // .append(StringPool.BACKTICK)
                    .append(column)
                    // .append(StringPool.BACKTICK)
                    .append(StringPool.SPACE).append(table.getTableName())
                    .append(StringPool.UNDERSCORE).append(StringPool.UNDERSCORE)
                    .append(column);
        }
        
        selectColumns = aliasColums.toString();
        
        if (!queryWrapper) {
            return selectColumns;
        }
        return SqlScriptUtils.convertChoose(String.format("%s != null and %s != null",
            Constants.WRAPPER, Constants.Q_WRAPPER_SQL_SELECT),
            SqlScriptUtils.unSafeParam(Constants.Q_WRAPPER_SQL_SELECT), selectColumns);
    }
    
    /*// ------------ 处理逻辑删除条件过滤 ------------
    
    @Override
    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
        return sqlWhereEntityWrapper(newLine, table, null);
    }
    
    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table, Class<?> modelClass) {
        if (table.isLogicDelete()) {
            String sqlScript = table.getAllSqlWhere(true, true, Constants.WRAPPER_ENTITY_DOT);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", Constants.WRAPPER_ENTITY), true);
            sqlScript += StringPool.NEWLINE + getAliasLogicDeleteSql(table) + StringPool.NEWLINE;
            
    //            //如果租户ID存在, 增加租户ID过滤
    //            if (null != modelClass && null != TableUtils.getTenantId(modelClass)) {
    //                String sqlTntId = " AND " + TableUtils.getTenantId(modelClass) + " = ${%s}";
    //                sqlScript += SqlScriptUtils.convertIf(String.format(sqlTntId, Constants.WRAPPER_DOT + MybatisxConstants.TNT_ID), 
    //                        String.format("%s != null", Constants.WRAPPER_DOT + MybatisxConstants.TNT_ID,
    //                                Constants.WRAPPER_DOT + MybatisxConstants.TNT_ID), true);
    //                sqlScript += StringPool.NEWLINE;
    //            }
            
            sqlScript += SqlScriptUtils.convertIf(String.format(" AND ${%s}", WRAPPER_SQLSEGMENT),
                    String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                        WRAPPER_NONEMPTYOFWHERE), true);
            sqlScript += StringPool.NEWLINE;
            
            
            // sqlScript = SqlScriptUtils.convertWhere(sqlScript) + NEWLINE;
            sqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                    WRAPPER_EMPTYOFWHERE), true);
            
            //sqlScript += StringPool.NEWLINE;
            sqlScript = SqlScriptUtils.convertChoose("ew != null", sqlScript, table.getLogicDeleteSql(true, false));
            
            sqlScript = SqlScriptUtils.convertTrim(sqlScript, "WHERE", null, "AND|OR", null);
            return newLine ? NEWLINE + sqlScript : sqlScript;
        }
        // 正常逻辑
        return super.sqlWhereEntityWrapper(newLine, table);
    }*/
    
    /*@Override
    protected String sqlWhereByMap(TableInfo table) {
        if (table.isLogicDelete()) {
            // 逻辑删除
            String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ",
                " ${k} = #{v} ");
            sqlScript = SqlScriptUtils.convertForeach(sqlScript, "cm", "k", "v", "AND");
            sqlScript = SqlScriptUtils.convertIf(sqlScript, "cm != null and !cm.isEmpty", true);
            sqlScript += (StringPool.NEWLINE + table.getLogicDeleteSql(true, false));
            sqlScript = SqlScriptUtils.convertTrim(sqlScript, "WHERE", null, "AND", null);
            return sqlScript;
        }
        return super.sqlWhereByMap(table);
    }*/
    
    /**
     * <p>
     * SQL 更新 set 语句
     * </p>
     *
     * @param logic  是否逻辑删除注入器
     * @param ew     是否存在 UpdateWrapper 条件
     * @param table  表信息
     * @param alias  别名
     * @param prefix 前缀
     * @return sql
     */
    protected String sqlAllSet(boolean logic, boolean ew, TableInfo table, String alias, String prefix) {
        // String sqlScript = SqlScriptUtils.convertIf(table.getAllSqlSet(logic, prefix), String.format("%s != null", alias), true);
        String sqlScript = getAllSqlSet(logic, prefix, table);
        if (ew) {
            sqlScript += NEWLINE + "";
            sqlScript += SqlScriptUtils.convertIf(SqlScriptUtils.unSafeParam(U_WRAPPER_SQL_SET),
                String.format("%s != null and %s != null", WRAPPER, U_WRAPPER_SQL_SET), false);
        }
        sqlScript = SqlScriptUtils.convertSet(sqlScript);
        return sqlScript;
    }
    
    private String getAllSqlSet(boolean ignoreLogicDelFiled, final String prefix, TableInfo table) {
        final String newPrefix = prefix == null ? EMPTY : prefix;
        return table.getFieldList().stream()
            .filter(i -> {
                if (i.getFieldFill() == FieldFill.INSERT) {
                    return false;
                }
                if (ignoreLogicDelFiled) {
                    return !(table.isWithLogicDelete() && i.isLogicDelete());
                }
                return true;
            }).map(i -> getSqlSet(newPrefix, i)).collect(joining(NEWLINE));
    }
    
    /**
     * 获取 set sql 片段
     *
     * @param prefix 前缀
     * @return sql 脚本片段
     */
    protected String getSqlSet(final String prefix, TableFieldInfo tableField) {
//        if (tableField.getFieldFill() == FieldFill.INSERT || tableField.getFieldFill() == FieldFill.INSERT_UPDATE) {
//            return "";
//        }
        final String newPrefix = prefix == null ? EMPTY : prefix;
        // 默认: column=
        String sqlSet = tableField.getColumn() + EQUALS;
        if (StringUtils.isNotBlank(tableField.getUpdate())) {
            sqlSet += String.format(tableField.getUpdate(), tableField.getColumn());
        } else {
            sqlSet += SqlScriptUtils.safeParam(newPrefix + tableField.getEl());
        }
        sqlSet += COMMA;
        // if (tableField.getFieldFill() == FieldFill.UPDATE || tableField.getFieldFill() == FieldFill.INSERT_UPDATE) {
            // 不进行 if 包裹
            // return sqlSet;
        // }
        return sqlSet;
    }
    
    /**
     * 支持获取动态表名
     * 
     * @param tableInfo
     * @return
     */
    protected String getDynamicTableName(TableInfo tableInfo) {
        return SqlScriptUtils.convertChoose(
                String.format("%s != null and %s != null", WRAPPER, WRAPPER_DOT + MybatisLinkConstants.TABLE_NAME), 
                String.format("${%s} %s", WRAPPER_DOT + MybatisLinkConstants.TABLE_NAME, tableInfo.getTableName()), 
                tableInfo.getTableName());
//        return SqlScriptUtils.convertChoose(
//                String.format("%s != null and %s != null and %s != null", 
//                        Constants.WRAPPER, 
//                        Constants.WRAPPER_DOT + MybatisxConstants.TABLE_NAME_MAP, 
//                        Constants.WRAPPER_DOT + MybatisxConstants.TABLE_NAME_MAP + Constants.DOT + tableInfo.getTableName()), 
//                String.format("${%s} %s", Constants.WRAPPER_DOT + MybatisxConstants.TABLE_NAME_MAP 
//                        + Constants.DOT + tableInfo.getTableName(), tableInfo.getTableName()), 
//                tableInfo.getTableName() + Constants.SPACE + tableInfo.getTableName());
    }
    
    /**
     * 获取自定义方法名，未设置采用默认方法名
     * https://gitee.com/baomidou/mybatis-plus/pulls/88
     *
     * @return method
     * @author 义陆无忧
     */
    public String getMethod(SoftSqlMethod sqlMethod) {
        return sqlMethod.getMethod();
    }
    
//    /**
//     * 
//     * 支持获取动态表名和别名
//     * 例如 t_sys_user t_sys_user 
//     * 
//     */
//    protected String getDynamicTableName(TableInfo tableInfo) {
//        String tableName = tableInfo.getTableName();
//        return SqlScriptUtils.convertChoose(
//                String.format("%s != null and %s != null and %s != null", 
//                        Constants.WRAPPER, 
//                        Constants.WRAPPER_DOT + MybatisxConstants.TB_MAP, 
//                        Constants.WRAPPER_DOT + MybatisxConstants.TB_MAP + Constants.DOT + tableName), 
//                String.format("${%s} %s", Constants.WRAPPER_DOT + MybatisxConstants.TB_MAP + Constants.DOT + tableName, tableName),
//                tableInfo.getTableName());
//    }
}
